﻿namespace HuwmanCode.ComponentModel.Composition.Hosting

open System
open System.Runtime.CompilerServices
open System.ComponentModel.Composition
open System.ComponentModel.Composition.Hosting
open System.ComponentModel.Composition.Primitives

module DefaultCompositionContainer =
    /// <summary>
    /// The interface which a CompositionContainer Initializer should implement.
    /// </summary>
    type ICompositionContainerInitializer =
        /// <summary>
        /// Provide the catlog.
        /// </summary>
        abstract ProvideCatalog: unit -> ComposablePartCatalog

        /// <summary>
        /// Provide the export providers.
        /// </summary>
        abstract ProvideExportProviders: unit -> ExportProvider array

    let private syncObj = new Object ()
    let private initializer' = ref { new ICompositionContainerInitializer with
                                        member this.ProvideCatalog () =
                                            let assemblyName = typeof<ICompositionContainerInitializer>.Assembly.GetName()
                                            new StrongNameCatalog (AppDomain.CurrentDomain.BaseDirectory, assemblyName.GetPublicKey ()) :> ComposablePartCatalog

                                        member this.ProvideExportProviders () =
                                            Array.empty }

    let private instance = 
        let initializer = lock syncObj ( fun _ -> !initializer')
        lazy ( new CompositionContainer (initializer.ProvideCatalog (), true, initializer.ProvideExportProviders ()))

    /// <summary>
    /// Gets the CompositionContainer singleton instance.
    /// </summary>
    let getInstance () = instance.Value

    /// <summary>
    /// Registers the CompositionContainer initializer.
    /// </summary>
    /// <param name="initializer">The CompositionContainer initializer.</param>
    let registerInitializer (initializer:ICompositionContainerInitializer) =
        lock syncObj ( fun _ ->
            let baseInitializer = !initializer'
            initializer' := { new ICompositionContainerInitializer with
                                member this.ProvideCatalog () =
                                    let baseCatalog = baseInitializer.ProvideCatalog ()
                                    let initCatalog = initializer.ProvideCatalog ()
                                    new AggregateCatalog (seq [baseCatalog;initCatalog]) :> ComposablePartCatalog

                                member this.ProvideExportProviders () =
                                    baseInitializer.ProvideExportProviders ()
                                    |> Array.append (initializer.ProvideExportProviders ()) })       

    

[<AutoOpen>]
module CompositionContainerExtension =
    type CompositionContainer with
        /// <summary>
        /// The CompositionContainer singleton instance
        /// </summary>
        static member Default = DefaultCompositionContainer.getInstance ()